package architecture;

//Pipeline_Architecture
public class Pipeline_Architecture{
	public PC pc;
	public InstructionMemory instructionMemory;
	public Registers registers;
	public MUX write_register_mux;
	public SignExtend signExtend;
	public MUX alu_mux;
	public ALU alu;

	public DataMemory dataMemory;
	public MUX writeback_mux;
	public ADDER pc_adder;
	public ADDER branch_adder;
	public JudgeZero judgeZero;
	public MUX pc_adder_mux;
	
	public PipelineRegister IF_ID_MUX;
	public PipelineInstructionRegister
							IF_ID_inst;
	
	public PipelineRegister ID_EX_read_data1;
	public PipelineRegister ID_EX_read_data2;
	public PipelineRegister ID_EX_signExtend;
	///////////////////////////////////////
	public PipelineRegister ID_EX_RegisterRs;
	public PipelineRegister ID_EX_RegisterRt;
	public PipelineRegister ID_EX_RegisterRd;

	public _ID_EX_Control_Buffer ID_EX_Control_Buffer;
	public _EX_MEM_Control_Buffer EX_MEM_Control_Buffer;
	public _MEM_WB_Control_Buffer MEM_WB_Control_Buffer;
	
	public PipelineRegister EX_MEM_ALU_result;
	public PipelineRegister EX_MEM_read_data2;
	public PipelineRegister EX_MEM_write_register;
	
	public PipelineRegister MEM_WB_read_data;
	public PipelineRegister MEM_WB_ALU_result;
	public PipelineRegister MEM_WB_write_register;
	
	public ControlUnit controlUnit;
	
	public _ID_ForwardingUnit ID_forwardingUnit;
	public _EX_ForwardingUnit EX_forwardingUnit;
	public _MEM_ForwardingUnit MEM_forwardingUnit;
	
	public MUX alu_forwardA_mux;
	public MUX alu_forwardB_mux;
	public MUX dataMemory_forwardC_mux;
	public MUX zero_forwardD_mux1;
	public MUX zero_forwardD_mux2;
	
	public ControlHazardUnit controlHazardUnit;
	
	public MyBoolean id_is_forwarding;
	public MyBoolean ex_is_forwarding;
	public MyBoolean mem_is_forwarding;
	
	public void variable_init(){
		//////////create obj
		pc=new PC();
		instructionMemory=new InstructionMemory();
		registers=new Registers();
		write_register_mux=new MUX();
		signExtend=new SignExtend();
		alu_mux=new MUX();
		alu=new ALU();
		dataMemory=new DataMemory();
		writeback_mux=new MUX();
		pc_adder=new ADDER();
		branch_adder=new ADDER();
		judgeZero=new JudgeZero();
		pc_adder_mux=new MUX();
		
		IF_ID_MUX=new PipelineRegister();
		IF_ID_inst=new PipelineInstructionRegister();
		
		ID_EX_read_data1=new PipelineRegister();
		ID_EX_read_data2=new PipelineRegister();
		ID_EX_signExtend=new PipelineRegister();
		ID_EX_RegisterRs=new PipelineRegister();
		ID_EX_RegisterRt=new PipelineRegister();
		ID_EX_RegisterRd=new PipelineRegister();
		
		ID_EX_Control_Buffer=new _ID_EX_Control_Buffer();
		EX_MEM_Control_Buffer=new _EX_MEM_Control_Buffer();
		MEM_WB_Control_Buffer=new _MEM_WB_Control_Buffer();
		
		EX_MEM_ALU_result=new PipelineRegister();
		EX_MEM_read_data2=new PipelineRegister();
		EX_MEM_write_register=new PipelineRegister();
		MEM_WB_read_data=new PipelineRegister();
		MEM_WB_ALU_result=new PipelineRegister();
		MEM_WB_write_register=new PipelineRegister();
		
		controlUnit=new ControlUnit();
		ID_forwardingUnit=new _ID_ForwardingUnit();
		EX_forwardingUnit=new _EX_ForwardingUnit();
		MEM_forwardingUnit=new _MEM_ForwardingUnit();
		controlHazardUnit=new ControlHazardUnit();
		
		alu_forwardA_mux=new MUX();
		alu_forwardB_mux=new MUX();
		
		dataMemory_forwardC_mux=new MUX();
		zero_forwardD_mux1=new MUX();
		zero_forwardD_mux2=new MUX();
		
		id_is_forwarding=new MyBoolean(false);
		ex_is_forwarding=new MyBoolean(false);
		mem_is_forwarding=new MyBoolean(false);
	}
	
	public void link(){
		//////////make link
		pc.write_pc=pc_adder_mux.result;
		
		pc_adder.src1.i=4;
		pc_adder.src2=pc.read_pc;
		
		pc_adder_mux.src0=pc_adder.result;
		pc_adder_mux.src1=branch_adder.result;
		pc_adder_mux.cond=judgeZero.result;
		
		instructionMemory.read_address=pc.read_pc;
		
		IF_ID_inst.instruction=instructionMemory.instruction;
		IF_ID_MUX.src=pc_adder_mux.result;
		///////////////////////////////////////////
		
		registers.read_register1=IF_ID_inst.instr_25_21;
		registers.read_register2=IF_ID_inst.instr_20_16;
		registers.write_register=MEM_WB_read_data.result;
		registers.write_data=writeback_mux.result;
		registers.regWrite=MEM_WB_Control_Buffer.regWrite_WB;
		
		signExtend.src=IF_ID_inst.instr_15_0;
		judgeZero.src1=registers.read_data1;
		judgeZero.src2=registers.read_data2;
		judgeZero.opCode=IF_ID_inst.instr_31_26;
		
		branch_adder.src1=IF_ID_MUX.result;
		branch_adder.src2=signExtend.result;
		
		ID_EX_read_data1.src=registers.read_data1;
		ID_EX_read_data2.src=registers.read_data2;
		
		ID_EX_signExtend.src=signExtend.result;
		
		ID_EX_RegisterRs.src=IF_ID_inst.instr_25_21;
		ID_EX_RegisterRt.src=IF_ID_inst.instr_20_16;
		ID_EX_RegisterRd.src=IF_ID_inst.instr_15_11;
		
		controlUnit.opCode=IF_ID_inst.instr_31_26;
		controlUnit.funcCode=IF_ID_inst.instr_5_0;
		
		ID_EX_Control_Buffer.ALUOp_ID=controlUnit.ALUOp;
		ID_EX_Control_Buffer.ALUSrc_ID=controlUnit.ALUSrc;
		ID_EX_Control_Buffer.memRead_ID=controlUnit.memRead;
		ID_EX_Control_Buffer.memToReg_ID=controlUnit.memToReg;
		ID_EX_Control_Buffer.memWrite_ID=controlUnit.memWrite;
		ID_EX_Control_Buffer.regWrite_ID=controlUnit.regWrite;
		ID_EX_Control_Buffer.regDst_ID=controlUnit.regDst;
		///////////////////////////////////////////
		
		alu_mux.src0=ID_EX_read_data2.result;
		alu_mux.src1=ID_EX_signExtend.result;
		alu_mux.cond=ID_EX_Control_Buffer.ALUSrc_EX;
		
		alu.src1=ID_EX_read_data1.result;
		alu.src2=alu_mux.src1;
		alu.ALUOp=ID_EX_Control_Buffer.ALUOp_EX;
		
		EX_MEM_ALU_result.src=alu.result;
		
		EX_MEM_read_data2.src=ID_EX_read_data2.result;
		
		write_register_mux.src0=ID_EX_RegisterRt.result;
		write_register_mux.src1=ID_EX_RegisterRd.result;
		write_register_mux.cond=ID_EX_Control_Buffer.regDst_EX;
		
		EX_MEM_Control_Buffer.memRead_EX=ID_EX_Control_Buffer.memRead_EX;
		EX_MEM_Control_Buffer.memToReg_EX=ID_EX_Control_Buffer.memToReg_EX;
		EX_MEM_Control_Buffer.memWrite_EX=ID_EX_Control_Buffer.memWrite_EX;
		EX_MEM_Control_Buffer.regWrite_EX=ID_EX_Control_Buffer.regWrite_EX;
		///////////////////////////////////////////
		
		dataMemory.address=EX_MEM_ALU_result.result;
		dataMemory.write_data=EX_MEM_read_data2.result;
		dataMemory.memWrite=EX_MEM_Control_Buffer.memWrite_MEM;
		dataMemory.memRead=EX_MEM_Control_Buffer.memRead_MEM;
		
		MEM_WB_read_data.src=dataMemory.read_data;
		
		MEM_WB_ALU_result.src=EX_MEM_ALU_result.result;
		
		MEM_WB_write_register.src=EX_MEM_write_register.result;
		
		MEM_WB_Control_Buffer.memToReg_MEM=EX_MEM_Control_Buffer.memToReg_MEM;
		MEM_WB_Control_Buffer.regWrite_MEM=EX_MEM_Control_Buffer.regWrite_MEM;
		///////////////////////////////////////////
		
		writeback_mux.src1=MEM_WB_read_data.result;
		writeback_mux.src0=MEM_WB_ALU_result.result;
		writeback_mux.cond=MEM_WB_Control_Buffer.memToReg_WB;
		
	}
	public void addForwarding(){
		///////////////////////////////////////////
		//Forwarding
		ID_forwardingUnit.EX_MEM_RegisterRd=EX_MEM_write_register.src;
		ID_forwardingUnit.EX_MEM_RegWrite=EX_MEM_Control_Buffer.regWrite_EX;
		ID_forwardingUnit.ID_EX_RegisterRd=ID_EX_RegisterRd.src;
		ID_forwardingUnit.ID_EX_RegWrite=ID_EX_Control_Buffer.regWrite_ID;
		ID_forwardingUnit.IF_ID_RegisterRs=instructionMemory.rs;
		ID_forwardingUnit.IF_ID_RegisterRt=instructionMemory.rt;
		ID_forwardingUnit.MEM_WB_MemToReg=MEM_WB_Control_Buffer.memToReg_MEM;
		ID_forwardingUnit.MEM_WB_RegisterRd=MEM_WB_write_register.src;
		ID_forwardingUnit.MEM_WB_RegWrite=MEM_WB_Control_Buffer.regWrite_MEM;
		ID_forwardingUnit.id_is_forwarding=id_is_forwarding;
		
		EX_forwardingUnit.EX_MEM_RegisterRd=EX_MEM_write_register.src;
		EX_forwardingUnit.EX_MEM_RegWrite=EX_MEM_Control_Buffer.regWrite_EX;
		EX_forwardingUnit.ID_EX_RegisterRs=ID_EX_RegisterRs.src;
		EX_forwardingUnit.ID_EX_RegisterRt=ID_EX_RegisterRt.src;
		EX_forwardingUnit.MEM_WB_MemToReg=MEM_WB_Control_Buffer.memToReg_MEM;
		EX_forwardingUnit.MEM_WB_RegisterRd=MEM_WB_write_register.src;
		EX_forwardingUnit.MEM_WB_RegWrite=MEM_WB_Control_Buffer.regWrite_MEM;
		EX_forwardingUnit.ex_is_forwarding=ex_is_forwarding;
		
		MEM_forwardingUnit.MEM_WB_MemToReg=MEM_WB_Control_Buffer.memToReg_MEM;
		MEM_forwardingUnit.MEM_WB_RegisterRd=MEM_WB_write_register.src;
		MEM_forwardingUnit.MEM_WB_RegWrite=MEM_WB_Control_Buffer.regWrite_MEM;
		MEM_forwardingUnit.mem_is_forwarding=mem_is_forwarding;
		
		alu_forwardA_mux.cond=EX_forwardingUnit.forwardA;
		alu_forwardA_mux.src0=ID_EX_read_data1.result;
		alu_forwardA_mux.src1=MEM_WB_read_data.result;
		alu_forwardA_mux.src2=MEM_WB_ALU_result.result;
		alu_forwardA_mux.src3=EX_MEM_ALU_result.result;
		alu.src1=alu_forwardA_mux.result;
		
		alu_forwardB_mux.cond=EX_forwardingUnit.forwardB;
		alu_forwardB_mux.src0=alu_mux.result;
		alu_forwardB_mux.src1=MEM_WB_read_data.result;
		alu_forwardB_mux.src2=MEM_WB_ALU_result.result;
		alu_forwardB_mux.src3=EX_MEM_ALU_result.result;
		alu.src2=alu_forwardB_mux.result;
		
		dataMemory_forwardC_mux.cond=MEM_forwardingUnit.forwardC;
		dataMemory_forwardC_mux.src0=EX_MEM_read_data2.result;
		dataMemory_forwardC_mux.src1=MEM_WB_read_data.result;
		dataMemory_forwardC_mux.src2=MEM_WB_ALU_result.result;
		dataMemory.write_data=dataMemory_forwardC_mux.result;
		
		zero_forwardD_mux1.cond=ID_forwardingUnit.forwardD1;
		zero_forwardD_mux1.src0=registers.read_data1;
		zero_forwardD_mux1.src1=MEM_WB_read_data.result;
		zero_forwardD_mux1.src2=MEM_WB_ALU_result.result;
		zero_forwardD_mux1.src3=EX_MEM_ALU_result.result;
		judgeZero.src1=zero_forwardD_mux1.result;
		
		zero_forwardD_mux2.cond=ID_forwardingUnit.forwardD2;
		zero_forwardD_mux2.src0=registers.read_data2;
		zero_forwardD_mux2.src1=MEM_WB_read_data.result;
		zero_forwardD_mux2.src2=MEM_WB_ALU_result.result;
		zero_forwardD_mux2.src3=EX_MEM_ALU_result.result;
		judgeZero.src2=zero_forwardD_mux2.result;
		
	}
	
	public void addStall() {
		///////////////////////////////////////////
	    //Stall
		controlHazardUnit.opCode=instructionMemory.opCode;
		///////////////////////////////////////////
	}
	void clockEvent_IF() {
		pc.clockEvent();
		instructionMemory.clockEvent();
		pc_adder.clockEvent();
		pc_adder_mux.clockEvent();
	}
	void clockEvent_ID(){
		IF_ID_MUX.clockEvent();
		IF_ID_inst.clockEvent();
		controlUnit.clockEvent();
		signExtend.clockEvent();
		registers.clockEvent();
		zero_forwardD_mux1.clockEvent();
		zero_forwardD_mux2.clockEvent();
		judgeZero.clockEvent();
		branch_adder.clockEvent();
	}
	void clockEvent_EX(){
		ID_EX_read_data1.clockEvent();
		ID_EX_read_data2.clockEvent();
		ID_EX_signExtend.clockEvent();
		ID_EX_RegisterRs.clockEvent();
		ID_EX_RegisterRt.clockEvent();
		ID_EX_RegisterRd.clockEvent();
		ID_EX_Control_Buffer.clockEvent();
		//////////////////////////////////
		alu_mux.clockEvent();
		alu_forwardA_mux.clockEvent();
		alu_forwardB_mux.clockEvent();
		//////////////////////////////////
		write_register_mux.clockEvent();
		
		alu.clockEvent();
	}
	void clockEvent_MEM(){
		EX_MEM_ALU_result.clockEvent();
		EX_MEM_read_data2.clockEvent();
		EX_MEM_write_register.clockEvent();
		EX_MEM_Control_Buffer.clockEvent();
		dataMemory_forwardC_mux.clockEvent();
		dataMemory.clockEvent();
	}
	void clockEvent_WB() {
		MEM_WB_read_data.clockEvent();
		MEM_WB_ALU_result.clockEvent();
		MEM_WB_write_register.clockEvent();
		MEM_WB_Control_Buffer.clockEvent();
		writeback_mux.clockEvent();
	}
	public void clockEvent() {
		controlHazardUnit.clockEvent();
		if(!controlHazardUnit.judgeResult){
			clockEvent_IF();
		}
		else System.out.println("IF stall!");
		if(!ID_forwardingUnit.forwardingFailureD1
				&&!ID_forwardingUnit.forwardingFailureD2)
			clockEvent_ID();
		else System.out.println("ID stall!");
		if(!EX_forwardingUnit.forwardingFailureA
				&&!EX_forwardingUnit.forwardingFailureB)
			clockEvent_EX();
		else System.out.println("EX stall!");
		if(!MEM_forwardingUnit.forwardingFailureC)
			clockEvent_MEM();
		else System.out.println("MEM stall!");
		clockEvent_WB();
		//////////////////////////////////////////
		//////////////////////////////////////////
		//////////////////////////////////////////
		//////////////////////////////////////////
	}
	public void setForwarding(boolean id_f,boolean ex_f,
			boolean mem_f){
		id_is_forwarding.b=id_f;
		ex_is_forwarding.b=ex_f;
		mem_is_forwarding.b=mem_f;
	}
}